using UnityEngine;

/// <summary>
/// 基于物理系统的鼠标方向跟随控制器
/// 实现原理：通过检测鼠标移动距离计算输入力，施加到Rigidbody组件实现自然运动
/// 核心特性：支持XZ平面限制、速度衰减、最大速度控制和输入死区过滤
/// 使用说明：挂载到需要跟随鼠标移动的物体上，确保物体具有Rigidbody组件
/// </summary>
[RequireComponent(typeof(Rigidbody))] // 需要Rigidbody组件实现物理效果
public class MouseDirectionFollower : MonoBehaviour
{
    #region 可配置参数

    [Header("物理设置")] // 在Inspector中创建标题分组
    [Tooltip("力的放大系数，值越大施加的力越强，直接影响鼠标灵敏度")] // 鼠标悬停提示
    [Range(10f, 100f)] // 限制取值范围
    public float forceMultiplier = 30f;

    [Tooltip("速度衰减系数，值越大减速越快，模拟摩擦力效果，建议值2-5")]
    [Range(0.5f, 10f)]
    public float damping = 2f;

    [Header("移动设置")]
    [Tooltip("是否将移动限制在XZ平面（忽略Y轴）")]
    public bool constrainToXZPlane = true;

        [Header("屏幕边界处理")]
        [Tooltip("鼠标输入灵敏度系数，用于调整输入的响应强度")]
        [Range(1f, 50f)]
        public float mouseSensitivity = 10f;
    [Range(0.01f, 0.5f)]
    public float inputDeadzone = 0.1f;

    [Tooltip("最大速度限制")]
    [Range(1f, 20f)]
    public float maxSpeed = 10f;

    #endregion

    #region 私有变量

    private Rigidbody _rigidbody; // 缓存Rigidbody组件，用于物理运动控制
    private Vector3 _inputForce; // 鼠标输入产生的力向量，每帧由鼠标移动距离计算
    private Vector3 _lastMousePosition; // 上一帧鼠标位置，用于计算鼠标移动增量

    #endregion

    #region 生命周期方法

    /// <summary>
/// 初始化组件引用和物理属性
/// 在对象实例化时调用一次，早于Start方法
/// </summary>
    private void Awake()
    {
        // 获取并缓存Rigidbody组件
        _rigidbody = GetComponent<Rigidbody>();
        
        // 配置物理属性以适应跟随行为
        _rigidbody.useGravity = false; // 禁用重力
        // 冻结Y轴位置和所有旋转，确保物体只在XZ平面移动
        _rigidbody.constraints = RigidbodyConstraints.FreezePositionY | RigidbodyConstraints.FreezeRotation;
        _lastMousePosition = Input.mousePosition; // 初始化鼠标位置
    }

    /// <summary>
    /// 每帧更新鼠标输入
    /// </summary>
    private void Update()
    {
        // 获取鼠标移动输入
        GetMouseInput();
    }

    /// <summary>
    /// 固定时间间隔更新物理逻辑
    /// </summary>
    private void FixedUpdate()
    {
        // 处理物理移动
        HandlePhysicsMovement();
    }

    #endregion

    #region 输入处理

    /// <summary>
/// 获取并处理鼠标移动输入，计算输入力
/// 处理流程：
/// 1. 使用相对鼠标输入或屏幕环绕技术获取移动增量
/// 2. 将鼠标移动距离转换为力向量
/// 3. 应用死区过滤消除微小输入
/// 4. 根据鼠标灵敏度和力放大系数计算最终输入力
/// </summary>
    private void GetMouseInput()
    {
        // 使用相对鼠标输入(不受屏幕边界限制)
        // Input.GetAxis返回的是标准化的相对移动量，不受屏幕边界限制
        float mouseX = Input.GetAxis("Mouse X");
        float mouseY = Input.GetAxis("Mouse Y");
        Vector3 mouseDelta = new Vector3(mouseX, 0f, mouseY) * mouseSensitivity;

        // 根据鼠标移动距离创建力向量(X轴:水平移动, Z轴:垂直移动)
        _inputForce = new Vector3(mouseDelta.x, 0f, mouseDelta.z);

        // 应用死区过滤：如果输入向量长度小于死区阈值，则视为无输入
        if (_inputForce.magnitude < inputDeadzone)
        {
            _inputForce = Vector3.zero;
        }
        else
        {
            // 根据鼠标移动距离计算力的大小，应用力放大系数
            _inputForce = _inputForce.normalized * (_inputForce.magnitude * forceMultiplier * Time.deltaTime);
        }
    }

    /// <summary>
    /// 检查鼠标位置并处理屏幕环绕逻辑
    /// 当鼠标移动到屏幕边缘时，将其位置重置到屏幕另一侧
    /// </summary>
    private void CheckScreenWrapping()
    {
        Vector3 currentMousePos = Input.mousePosition;
        bool wrapped = false;

        // 检查水平边界
        if (currentMousePos.x <= 0)
        {
            currentMousePos.x = Screen.width - 1;
            wrapped = true;
        }
        else if (currentMousePos.x >= Screen.width - 1)
        {
            currentMousePos.x = 0;
            wrapped = true;
        }

        // 检查垂直边界
        if (currentMousePos.y <= 0)
        {
            currentMousePos.y = Screen.height - 1;
            wrapped = true;
        }
        else if (currentMousePos.y >= Screen.height - 1)
        {
            currentMousePos.y = 0;
            wrapped = true;
        }

        // 如果发生了屏幕环绕，更新鼠标位置和最后记录位置
        if (wrapped)
        {
            _lastMousePosition = currentMousePos;
        }
    }

    #endregion

    #region 物理移动逻辑

    /// <summary>
/// 处理基于物理的移动逻辑
/// 核心功能：
/// 1. 当存在输入力时，将力施加到刚体上
/// 2. 当无输入时，应用阻尼效果使物体逐渐减速
/// 3. 始终限制物体速度不超过最大速度
/// 物理原理：使用AddForce施加力，通过Lerp实现平滑减速
/// </summary>
    private void HandlePhysicsMovement()
    {
        // 应用输入力到刚体，实现鼠标控制的移动
        if (_inputForce != Vector3.zero)
        {
            Vector3 force = _inputForce;

            // 如果限制在XZ平面，则确保Y轴不受力，防止物体上下移动
            if (constrainToXZPlane)
            {
                force.y = 0f;
            }

            // 施加力到刚体
            _rigidbody.AddForce(force);
        }
        // 没有输入时应用阻尼，模拟自然减速效果
        else
        {
            // 使用Lerp实现平滑减速，Time.fixedDeltaTime确保减速速率与帧率无关
            _rigidbody.velocity = Vector3.Lerp(
                _rigidbody.velocity,
                Vector3.zero,
                damping * Time.fixedDeltaTime
            );
        }

        // 限制最大速度，防止物体移动过快失去控制
        if (_rigidbody.velocity.magnitude > maxSpeed)
        {
            _rigidbody.velocity = _rigidbody.velocity.normalized * maxSpeed;
        }
    }

    #endregion

    #region 调试辅助

    /// <summary>
/// 在Scene视图中绘制移动方向Gizmo
/// 调试辅助功能：直观显示物体当前移动方向和速度大小
/// 绘制规则：
/// - 仅当Rigidbody存在且速度不为零时绘制
/// - 使用青色箭头表示移动方向
/// - 箭头长度与当前速度成正比（缩放系数0.1）
/// 使用方法：在Scene窗口中选择物体即可看到方向指示
/// </summary>
    private void OnDrawGizmos()
    {
        if (_rigidbody == null || _rigidbody.velocity == Vector3.zero) return;

        // 设置gizmo颜色为青色，便于在Scene视图中识别
        Gizmos.color = Color.cyan;

        // 绘制速度方向箭头：起点为物体位置，方向为速度方向，长度与速度大小成正比
        // 绘制速度方向箭头
        Gizmos.DrawRay(
            transform.position,
            _rigidbody.velocity.normalized * _rigidbody.velocity.magnitude * 0.1f
        );
    }

    #endregion
}